home *** CD-ROM | disk | FTP | other *** search
- /* st_play.c
- vi:ts=3 sw=3:
- */
-
- /* $Id: st_play.c,v 4.9 1995/02/14 04:02:28 espie Exp $
- * $Log: st_play.c,v $
- * Revision 4.9 1995/02/14 04:02:28 espie
- * Suppressed comment.
- *
- * Revision 4.9 1995/02/14 04:02:28 espie
- * Suppressed comment.
- *
- * Revision 4.8 1995/02/06 14:50:47 espie
- * Changed sample_info.
- *
- * Revision 4.8 1995/02/06 14:50:47 espie
- * Changed sample_info.
- *
- * Revision 4.7 1995/02/01 20:41:45 espie
- * Added color.
- *
- * Revision 4.6 1995/02/01 16:39:04 espie
- * Left/Right channels.
- *
- * Revision 4.6 1995/02/01 16:39:04 espie
- * Left/Right channels.
- *
- * Revision 4.5 1995/02/01 15:31:25 espie
- * *** empty log message ***
- *
- * Revision 4.4 1995/01/28 09:23:59 espie
- * *** empty log message ***
- *
- * Revision 4.3 1994/11/15 16:11:01 espie
- * *** empty log message ***
- *
- * Revision 4.3 1994/11/15 16:11:01 espie
- * *** empty log message ***
- *
- *
- * Revision 4.0 1994/01/11 17:51:40 espie
- * Use the new UI calls.
- * Use the new pref settings.
- *
- * Revision 1.14 1994/01/09 23:24:37 Espie
- * Last bug fix.
- * Suppressed really outdated code.
- * New names: new_channel_tag_list, release_audio_channel.
- * Some notice to status.
- * Use new pref scheme.
- * Use get_ui
- * Use autoinit feature of display.c
- * discard_buffer forgotten...
- * Handle errors better.
- *
- * Revision 3.18 1993/12/04 16:12:50 espie
- * Lots of changes.
- * New high-level functions.
- * Amiga support.
- * Bug with delay_pattern: can't factorize the check for effect thingy.
- * Reniced verbose output display.
- * Bug fix: now use correct finetune when loading samples/starting notes.
- * Added bg/fg test.
- * General cleanup
- * Added <> operators.
- * Added update frequency on the fly.
- * Added finetune.
- * Protracker commands.
- *
- * Revision 2.19 1992/11/17 17:15:37 espie
- * Added interface using may_getchar(). Still primitive, though.
- * imask, start.
- * Added transpose feature.
- * Added possibility to get back to MONO for the sgi.
- * Added stereo capabilities to the indigo version.
- * Added two level of fault tolerancy.
- * Added some control on the number of replays,
- * and better error recovery.
- */
-
- #include "defs.h"
- #include "song.h"
- #include "channel.h"
- #include "extern.h"
- #include "tags.h"
- #include "prefs.h"
-
-
- ID("$Id: st_play.c,v 4.9 1995/02/14 04:02:28 espie Exp $")
-
-
- /* setting up a given note */
-
- void reset_note(ch, note, pitch)
- struct channel *ch;
- int note;
- int pitch;
- {
- ch->pitch = pitch;
- ch->note = note;
- ch->viboffset = 0;
- play_note(ch->audio, ch->samp, pitch);
- }
-
- /* changing the current pitch (value
- * may be temporary, and not stored
- * in channel pitch, for instance vibratos.
- */
- void set_current_pitch(ch, pitch)
- struct channel *ch;
- int pitch;
- {
- /* save current pitch in case we want to change
- * the step table on the run
- ch->cpitch = pitch;
- ch->step = step_table[pitch];
- */
- set_play_pitch(ch->audio, pitch);
- }
-
- /* changing the current volume. You HAVE to get through
- * there so that it will work on EVERY machine.
- */
- void set_current_volume(ch, volume)
- struct channel *ch;
- int volume;
- {
- ch->volume = MAX(MIN(volume, MAX_VOLUME), MIN_VOLUME);
- set_play_volume(ch->audio, ch->volume);
- }
-
- void set_position(ch, pos)
- struct channel *ch;
- int pos;
- {
- set_play_position(ch->audio, pos);
- }
-
- /* init_channel(ch, dummy):
- * setup channel, with initially
- * a dummy sample ready to play,
- * and no note.
- */
- LOCAL void init_channel(ch, side)
- struct channel *ch;
- int side;
- {
- struct tag tags[2];
- tags[0].type = AUDIO_SIDE;
- tags[0].data.scalar = side;
- tags[1].type = TAG_END;
- ch->samp = empty_sample();
- ch->finetune = 0;
- ch->audio = new_channel_tag_list(tags);
- ch->volume = 0;
- ch->pitch = 0;
- ch->note = NO_NOTE;
-
- /* we don't setup arpeggio values. */
- ch->viboffset = 0;
- ch->vibdepth = 0;
-
- ch->slide = 0;
-
- ch->pitchgoal = 0;
- ch->pitchrate = 0;
-
- ch->volumerate = 0;
-
- ch->vibrate = 0;
- ch->adjust = do_nothing;
- }
-
-
-
- LOCAL int VSYNC; /* base number of sample to output */
- LOCAL void (*eval[NUMBER_EFFECTS]) P((struct automaton *a, struct channel *ch));
- /* the effect table */
- LOCAL int oversample; /* oversample value */
- LOCAL int frequency; /* output frequency */
-
- LOCAL struct channel chan[NUMBER_TRACKS];
- /* every channel */
-
- LOCAL struct sample_info **voices;
-
- LOCAL struct automaton a;
-
-
- void init_player(o, f)
- int o, f;
- {
- oversample = o;
- frequency = f;
- init_tables(o, f);
- init_effects(eval);
- }
-
- LOCAL void setup_effect(ch, a, e)
- struct channel *ch;
- struct automaton *a;
- struct event *e;
- {
- int samp, cmd;
-
- /* retrieves all the parameters */
- samp = e->sample_number;
-
- /* load new instrument */
- if (samp)
- {
- /* note that we can change sample in the middle
- * of a note. This is a *feature*, not a bug (see
- * made). Precisely: the sample change will be taken
- * into account for the next note, BUT the volume change
- * takes effect immediately.
- */
- ch->samp = voices[samp];
- ch->finetune = voices[samp]->finetune;
- if ((1L<<samp) & get_pref_scalar(PREF_IMASK))
- ch->samp = empty_sample();
- set_current_volume(ch, voices[samp]->volume);
- }
-
- a->note = e->note;
- if (a->note != NO_NOTE)
- a->pitch = pitch_table[a->note][ch->finetune];
- else
- a->pitch = e->pitch;
- cmd = e->effect;
- a->para = e->parameters;
-
- if (a->pitch >= REAL_MAX_PITCH)
- {
- char buffer[60];
- sprintf(buffer,"Pitch out of bounds %d", a->pitch);
- status(buffer);
- a->pitch = 0;
- error = FAULT;
- }
-
- /* check for a new note: portamento
- * is the special case where we do not restart
- * the note.
- */
- if (a->pitch && cmd != EFF_PORTA && cmd != EFF_PORTASLIDE)
- reset_note(ch, a->note, a->pitch);
- ch->adjust = do_nothing;
- /* do effects */
- (eval[cmd])(a, ch);
- }
-
-
- LOCAL void adjust_sync(ofreq, tempo)
- int ofreq, tempo;
- {
- VSYNC = ofreq * NORMAL_FINESPEED / tempo;
- }
-
- LOCAL void play_once(a)
- struct automaton *a;
- {
- int channel;
-
- if (a->do_stuff & DELAY_PATTERN)
- for (channel = 0; channel < NUMBER_TRACKS; channel++)
- /* do the effects */
- (chan[channel].adjust)(chan + channel);
- else
- {
- if (a->counter == 0)
- {
- for (channel = 0; channel < NUMBER_TRACKS; channel++)
- /* setup effects */
- setup_effect(chan + channel, a,
- &(a->pattern->e[channel][a->note_num]));
- if (get_pref_scalar(PREF_SHOW))
- {
- /* display the output in a reasonable order:
- * LEFT1 LEFT2 || RIGHT1 RIGHT 2
- */
- dump_event(chan, &(a->pattern->e[0][a->note_num]));
- dump_event(chan+3, &(a->pattern->e[3][a->note_num]));
- dump_delimiter();
- dump_event(chan+1, &(a->pattern->e[1][a->note_num]));
- dump_event(chan+2, &(a->pattern->e[2][a->note_num]));
- dump_event(0, 0);
- }
- }
- else
- for (channel = 0; channel < NUMBER_TRACKS; channel++)
- /* do the effects */
- (chan[channel].adjust)(chan + channel);
- }
-
- /* advance player for the next tick */
- next_tick(a);
- /* actually output samples */
- resample(oversample, VSYNC / a->finespeed);
- }
-
- LOCAL struct tag pres[2];
-
-
- struct tag *play_song(song, start)
- struct song *song;
- int start;
- {
- int tempo;
- int countdown; /* keep playing the tune or not */
-
- song_title(song->title);
- pres[1].type = TAG_END;
-
- tempo = get_pref_scalar(PREF_SPEED);
-
- adjust_sync(frequency, tempo);
- /* a repeats of 0 is infinite replays */
-
- countdown = get_pref_scalar(PREF_REPEATS);
- if (countdown == 0)
- countdown = 50000; /* ridiculously huge number */
-
- voices = song->samples;
-
- init_automaton(&a, song, start);
-
- release_audio_channels();
-
- init_channel(chan, LEFT_SIDE);
- init_channel(chan + 1, RIGHT_SIDE);
- init_channel(chan + 2, RIGHT_SIDE);
- init_channel(chan + 3, LEFT_SIDE);
-
- while(countdown)
- {
- struct tag *result;
-
- play_once(&a);
- result = get_ui();
- while(result = get_tag(result))
- {
- switch(result->type)
- {
- case UI_LOAD_SONG:
- if (!result->data.pointer)
- break;
- case UI_NEXT_SONG:
- case UI_PREVIOUS_SONG:
- discard_buffer();
- pres[0].type = result->type;
- pres[0].data = result->data;
- return pres;
- case UI_QUIT:
- discard_buffer();
- end_all(0);
- /* NOTREACHED */
- case UI_SET_BPM:
- tempo = result->data.scalar;
- adjust_sync(frequency, tempo);
- break;
- case UI_RESTART:
- discard_buffer();
- init_automaton(&a, song, start);
- release_audio_channels();
- init_channel(chan, LEFT_SIDE);
- init_channel(chan + 1, RIGHT_SIDE);
- init_channel(chan + 2, RIGHT_SIDE);
- init_channel(chan + 3, LEFT_SIDE);
- break;
- case UI_JUMP_TO_PATTERN:
- if (result->data.scalar >= 0 && result->data.scalar < a.info->length)
- {
- discard_buffer();
- init_automaton(&a, song, result->data.scalar);
- }
- break;
- /*
- case ' ':
- while (may_getchar() == EOF)
- ;
- break;
- */
- default:
- break;
- }
- result++;
- }
-
- {
- int new_freq;
- if (new_freq = update_frequency())
- {
- frequency = new_freq;
- adjust_sync(frequency, tempo);
- init_tables(oversample, frequency);
- }
- }
-
- switch(error)
- {
- case NONE:
- break;
- case ENDED:
- countdown--;
- break;
- case SAMPLE_FAULT:
- case FAULT:
- case PREVIOUS_SONG:
- case NEXT_SONG:
- case UNRECOVERABLE:
- if ( (error == SAMPLE_FAULT && get_pref_scalar(PREF_TOLERATE))
- ||(error == FAULT && get_pref_scalar(PREF_TOLERATE) > 1) )
- break;
- pres[0].type = PLAY_ERROR;
- pres[0].data.scalar = error;
- return pres;
- default:
- break;
- }
- error = NONE;
- }
- pres[0].type = TAG_IGNORE;
- return pres;
- }
-
-